// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CXFormsXPathFunctions.
//   $Id$



// XFormsXPathFunctions.cpp : Implementation of CXFormsXPathFunctions
#include "stdafx.h"
#include "XForms.h"
#include "XFormsXPathFunctions.h"
#include "Model.h"
#include <limits>
#include "dateTime.h"
#include "FP_ReturnCodes.h"
#include <new>
#include "eventmsg.h"
#include "XFormsApp.h"
#include "implInstance.h"
#include "repeat.h"
#include <time.h>
#include <Common/ResourceHandling/msw/loadHTMLResource.h>
#include "entrypoint.h"
#include <luhn.h>

const unsigned int g_ui_luhn_min_length = 12;
const unsigned int g_ui_luhn_max_length = 19;

extern CXFormsApp theApp;

//defining try/catch blocks for this document
//see entrypoint.h for original definitions.
#ifdef _DEBUG_CATCH_XPATH_FUNCTION_EXCEPTIONS_AT_SOURCE
	#define FORMSPLAYER_ENTRY_POINT_FROM_XPATH
	#define FORMSPLAYER_EXIT_POINT_TO_XPATH
#else
	#define FORMSPLAYER_ENTRY_POINT_FROM_XPATH FORMSPLAYER_ENTRY_POINT
	#define FORMSPLAYER_EXIT_POINT_TO_XPATH FORMSPLAYER_EXIT_POINT
#endif
		

const CComBSTR g_XPathFunction_Instance = L"instance";
/////////////////////////////////////////////////////////////////////////////
// CXFormsXPathFunctions

	//Override GetIdsOfNames to fix non-idl-token
	//	XPath method names

STDMETHODIMP CXFormsXPathFunctions::GetIDsOfNames
(
	REFIID riid,
	LPOLESTR* rgszNames,
	UINT cNames,
	LCID lcid,
	DISPID* rgdispid
)
{
	HRESULT hr = S_OK;
	try {

		LPOLESTR * newNames = new LPOLESTR[cNames];
		bool bAskIDispatch = false;
		for(unsigned int i = 0 ; i < cNames ; ++i)
		{
			//Instance is by far the most commonly called XPath function
			//	it is often called alone, 
			//	therefore, although there is no need to do any special processing for it
			//	it will improve performace to test it before going on to test the remainder
			//	of the function names.

			//TODO: Use a map
			// Now that there are more of these non-IDL-token function names in use,
			//	this method is going to get slower.
			// Implement this using an STL map, for quickness.

			if (g_XPathFunction_Instance == rgszNames[i])
			{
				if(cNames > 1)
					newNames[i] = L"instance";
				rgdispid[i] = 11;
			}
			else if (!wcscmp(rgszNames[i],L"boolean-from-string"))
			{
				newNames[i] = L"booleanFromString";
				rgdispid[i] = 9;
			}
			else if (!wcscmp(rgszNames[i],L"count-non-empty"))
			{
				newNames[i] = L"countNonEmpty";
				rgdispid[i] = 1;
			}
			else if (!wcscmp(rgszNames[i],L"days-from-date"))
			{
				newNames[i] = L"daysFromDate";
				rgdispid[i] = 12;
			}
			else if (!wcscmp(rgszNames[i],L"seconds-from-dateTime"))
			{
				newNames[i] = L"secondsFromDateTime";	
				rgdispid[i] = 13;
			}
			else if (!wcscmp(rgszNames[i],L"if") || !wcscmp(rgszNames[i],L"choose"))
			{
				newNames[i] = L"_if";
				rgdispid[i] = 10;
			}
			else if (!wcscmp(rgszNames[i],L"min"))
			{
				newNames[i] = L"_min";
				rgdispid[i] = 5;
			}
			else if (!wcscmp(rgszNames[i],L"max"))
			{
				newNames[i] = L"_max";		
				rgdispid[i] = 6;
			}
			else if (!wcscmp(rgszNames[i],L"id"))
			{
				hr = E_FAIL;
			}
			else if (!wcscmp(rgszNames[i],L"days-to-date"))
			{
				newNames[i] = L"daysToDate";		
				rgdispid[i] = 23;
			}
			else if (!wcscmp(rgszNames[i],L"seconds-to-dateTime"))
			{
				newNames[i] = L"secondsToDateTime";		
				rgdispid[i] = 24;
			}
			else if (!wcscmp(rgszNames[i],L"local-date"))
			{
				newNames[i] = L"localDate";		
				rgdispid[i] = 25;
			}
			else if (!wcscmp(rgszNames[i],L"local-dateTime"))
			{
				newNames[i] = L"localDateTime";		
				rgdispid[i] = 26;
			}
			
	/*		else if (!wcscmp(rgszNames[i],L"instance"))
			{
				newNames[i] = L"instance";
				rgdispid[i] = 16;

			}
	*/		else
			{
				bAskIDispatch = true;
				newNames[i] = rgszNames[i];
			}
		}

		if(bAskIDispatch)
			hr = CXFormsXPathFunctionsMultiIDispatch::GetIDsOfNames(riid,newNames,cNames,lcid,rgdispid);

		delete [] newNames;
		
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::countNonEmpty(IDispatch *pNodeSet, long *retval)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = S_OK;
	IXMLDOMSelection* pDOMSelection = NULL;
	try {
		hr = pNodeSet->QueryInterface(IID_IXMLDOMNodeList, (void**) &pDOMSelection);
		if(SUCCEEDED(hr))
		{
			//set Return to 0;
			*retval = 0;
			IXMLDOMNode * pNode = NULL;
			pDOMSelection->nextNode(&pNode);

			CComBSTR sText;
			//count the non-empty nodes in the selection.
			while(pNode)
			{
				pNode->get_text(&sText);
				//only increment count when get_text is not ""
				if(sText.m_str[0])
					(*retval)++;
				pNode->Release();
				pDOMSelection->nextNode(&pNode);
			}
		}
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	::safeRelease(pDOMSelection);
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::index(VARIANT vtId, double * retval)
{
	CComBSTR sId;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtId,&sId);
		if(SUCCEEDED(hr))
			hr = index(sId, retval);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::index(BSTR sId, double *retval)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = S_OK;
	CImplRepeat* pRepeat = NULL;

	//see if the supplied ID refers to a repeat in the form
	if(m_pOwnerForm)
	{
		hr = m_pOwnerForm->getRepeat(sId, &pRepeat);
		if(hr == S_OK && pRepeat)
		{
			ULONG ulCursor = pRepeat->getCursor();
			if(ulCursor > 0)
			{
				*retval = static_cast<double>(ulCursor);
			}
			else
			{
				hr = S_FALSE;
				*retval = std::numeric_limits<double>::quiet_NaN();
			}
		}
		else if(m_pOwnerForm->IsPendingRepeat(sId))
		{
			hr = S_OK;
			*retval = 1;
		}
	}
	else
		hr = E_FAIL;
	return hr;
}
/*
removed:
STDMETHODIMP CXFormsXPathFunctions::getInstanceDocument(BSTR sModelId, LPDISPATCH *ppDisp)
at version 24
*/


HRESULT CXFormsXPathFunctions::nodelistToNumber
(
	IDispatch * pNodeSet,
	double (__stdcall * pCompFunc)(double,double),
	double* dblRetVal
)
{
	//Used by the numeric functions for performing a comparison etc.
	//	on a list of numbers in a nodelist.
	HRESULT hr = S_OK;

	IXMLDOMNodeList * pNl = NULL;
	hr = pNodeSet->QueryInterface(IID_IXMLDOMNodeList, (void**) &pNl);

	if (SUCCEEDED(hr))
	{
		IXMLDOMNode * pNode = NULL;
		pNl->nextNode(&pNode);
		if(!pNode)
		{
			//NaN if empty list.
			*dblRetVal = std::numeric_limits<double>::quiet_NaN();//0/0;
		}
		else
		{
			CComBSTR sTemp;
			//set up Alpha
			pNode->get_text(&sTemp);
			double dblAlpha;
			hr = ::VarR8FromStr(sTemp,LOCALE_SYSTEM_DEFAULT,LOCALE_NOUSEROVERRIDE,&dblAlpha);
			sTemp.Empty();
			double dblCurrent;

			//run the loop.
			pNl->nextNode(&pNode);		
			while(pNode && hr != DISP_E_TYPEMISMATCH)
			{
				//get the text value of the node 
				//	and convert it to a double
				pNode->get_text(&sTemp);
				//	no mention is made regarding how to handle strings that do
				//	not convert to numbers, or how to convert them.
				//	therefore I am just using built in Automation conversion.
				hr = ::VarR8FromStr(sTemp,LOCALE_SYSTEM_DEFAULT,LOCALE_NOUSEROVERRIDE,&dblCurrent);
				
				//call the function on the new value, passing
				//	in the old stored value
				dblAlpha = pCompFunc(dblAlpha,dblCurrent);
				//prepare for next iteration
				::safeRelease(pNode);
				pNl->nextNode(&pNode);		
			}
			if(hr == DISP_E_TYPEMISMATCH)
				*dblRetVal = std::numeric_limits<double>::quiet_NaN();//0/0;
			else
				*dblRetVal = dblAlpha;
		}
	}
	::safeRelease(pNl);
	return hr;

}

STDMETHODIMP CXFormsXPathFunctions::avg(IDispatch *pNodeSet, double *dblRetVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	HRESULT hr = S_FALSE;
	try {
		CComQIPtr<IXMLDOMNodeList> pNS(pNodeSet);
		if(pNS)
		{
			double total;
			hr = nodelistToNumber(pNodeSet,addTogether,&total);
			if(!_isnan(total))
			{
				long l;
				pNS->get_length(&l);
				*dblRetVal = total / static_cast<double>(l);
				hr = S_OK;
			}
			else
			{
				*dblRetVal = total;
				hr = S_FALSE;
			}
		}
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}

	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::_min(IDispatch *pNodeSet, double *dblRetVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = E_FAIL;
	try {
		hr = nodelistToNumber(pNodeSet,getLowerOf,dblRetVal);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::_max(IDispatch *pNodeSet, double *dblRetVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = E_FAIL;
	try {
		hr = nodelistToNumber(pNodeSet,getHigherOf,dblRetVal);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::now(BSTR *psRetval)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = E_FAIL;
	try {
		
		time_t t_now = time(0);
		t_now += _timezone;
		COleDateTime oT = t_now;
		CString s = oT.Format("%Y-%m-%dT%H:%M:%S");
		s.Append("Z");
		*psRetval = s.AllocSysString();
		s.Empty();
		hr = S_OK;
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::property(VARIANT vtProp, BSTR * psVal)
{
	CComBSTR sProp;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtProp,&sProp);
		if(SUCCEEDED(hr))
			hr = property(sProp, psVal);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::property(BSTR prop, BSTR *val)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	HRESULT hr = S_OK;
	CComBSTR s;
	if(!wcscmp(prop,L"version"))
	{
		s.LoadString(IDS_XFormsVersion);
	}
	else if(!wcscmp(prop,L"conformance-level"))
	{
		s.LoadString(IDS_XFormsConformance);
	}
	else if(!wcscmp(prop,L"FormsPlayerVersion"))
	{
		s.LoadString(IDS_FormsPlayerVersion);
		char fullpath[MAX_PATH];
		::GetModuleFileName(_Module.m_hInst,fullpath,MAX_PATH);
		DWORD handle;
		DWORD dwSize =::GetFileVersionInfoSize(fullpath,&handle);
		void * info = new char[dwSize + 1];
		::GetFileVersionInfo(fullpath,handle,dwSize,info);
		void * version = 0;
		unsigned int iSize;
		::VerQueryValue(info,"\\StringFileInfo\\080904b0\\ProductVersion",(void**)&version,&iSize);
		s = CComBSTR((char*)version);
		delete[] info;
	}
	else
	{
		hr = E_INVALIDARG;
	}
	*val = s.Detach();

	return hr;
}

void GetVariantFromXPathResult(IXPathResult *pResult,VARIANT * pVariant)
{
	short sType;
	pResult->get_resultType(&sType);
	CComVariant vtRetval;
	switch(sType)
	{
		case BOOLEAN_TYPE :
		{
			BOOL BVal;
			pResult->get_booleanValue(&BVal);
			vtRetval.boolVal = BVal;
			vtRetval.vt = VT_BOOL;
			break;
		}
		case STRING_TYPE:
		{
			CComBSTR bsVal;
			pResult->get_stringValue(&bsVal);
			vtRetval.bstrVal = bsVal.Detach();
			vtRetval.vt = VT_BSTR;
			break;
		}
		case NUMBER_TYPE:
		{
			double dblVal;
			pResult->get_numberValue(&dblVal);
			vtRetval.dblVal = dblVal;
			vtRetval.vt = VT_R8;
			break;
		}
		//The rest of them are node or nodeset types of a sort.
		case  ANY_UNORDERED_NODE_TYPE:
		case  FIRST_ORDERED_NODE_TYPE:
		{
			CComPtr<IXMLDOMNode> pNode;
			pResult->get_singleNodeValue(&pNode);
			vtRetval = CComVariant(pNode);
		}
		case UNORDERED_NODE_SNAPSHOT_TYPE:
		case ORDERED_NODE_SNAPSHOT_TYPE:
		{
			CComPtr<IXMLDOMNode> pNode;
			pResult->snapshotItem(0,&pNode);
			vtRetval = CComVariant(pNode);
		}
		case UNORDERED_NODE_ITERATOR_TYPE:
		case ORDERED_NODE_ITERATOR_TYPE:
		{
			CComPtr<IXMLDOMNode> pNode;
			pResult->iterateNext(&pNode);
			vtRetval = CComVariant(pNode);			
		}
		

	}
	vtRetval.Detach(pVariant);
}

STDMETHODIMP CXFormsXPathFunctions::evaluate(VARIANT vtInput, VARIANT * pvtRetval)
{
	CComBSTR sExpr;
	HRESULT hr = getStringFromVariant(vtInput,&sExpr);
	if(SUCCEEDED(hr))
	{
		CComPtr<IXPathResult> pResult;
		CComQIPtr<IXMLDOMNode> pContext;
		//Get current context from stack
		if(m_XPathContextStack.size() > 0)
			pContext = m_XPathContextStack.top();
		//get instance data using context
		CImplModel * pActiveModel = m_ActiveModelStack.top();
		hr = pActiveModel->getInstanceData(sExpr,pContext,&pResult);
		//translate XPathResult into variant for return
		GetVariantFromXPathResult(pResult, pvtRetval);
		if(pvtRetval && pvtRetval->vt == VT_DISPATCH && pvtRetval->pdispVal == 0)
		{
			//MS XSLT doesn't like null, prefers empty nodeset
			CComPtr<IXMLDOMNodeList> pNodeset;
			pContext->selectNodes(L"/nodeThatDoesNotExist",&pNodeset);
			pvtRetval->pdispVal = pNodeset.Detach();
		}
	}

	return hr;
	
}

STDMETHODIMP CXFormsXPathFunctions::booleanFromString(VARIANT vtBool, VARIANT *vtReturn)
{
	CComBSTR sBool;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtBool,&sBool);
		if(sBool)
			hr = booleanFromString(sBool,vtReturn);
		else
			hr = E_FAIL;
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::booleanFromString(BSTR sBool, VARIANT *vtReturn)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	HRESULT hr = S_OK;
	vtReturn->vt = VT_BOOL;
	if(_wcsicmp(L"true",sBool) == 0 || _wcsicmp(L"1",sBool) == 0)
		vtReturn->boolVal = VARIANT_TRUE;
	else 
		//No exception any more, anything which is not true, is false.
		vtReturn->boolVal = VARIANT_FALSE;
	
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::_if(BOOL bCondition, VARIANT vtIfTrue, VARIANT vtIfFalse, BSTR *sRet)
{
	HRESULT hr = E_FAIL;
	try {
		if(bCondition)
			hr = getStringFromVariant(vtIfTrue,sRet);
		else
			hr = getStringFromVariant(vtIfFalse,sRet);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}
/*
version 33:
removed, no need now that variant method is employed.
STDMETHODIMP CXFormsXPathFunctions::_if(BOOL bCondition, BSTR bIfTrue, BSTR bIfFalse, BSTR *sRet)
*/

STDMETHODIMP CXFormsXPathFunctions::instance(VARIANT vtInstanceId, IXMLDOMNodeList ** ppRetval)
{
	CComBSTR sInstanceId;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtInstanceId,&sInstanceId);
		if(SUCCEEDED(hr))
		{
			hr = instance(sInstanceId, ppRetval);
		}
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::globalInstance(VARIANT vtInstanceId,/*[out,retval]*/ IXMLDOMNodeList ** ppRetval)
{
	CComBSTR sInstanceId;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtInstanceId,&sInstanceId);
		if(SUCCEEDED(hr))
		{
			hr = globalInstance(sInstanceId, ppRetval);
		}
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

const CComBSTR g_bsPathToDocumentElement(L"/*");

HRESULT CXFormsXPathFunctions::GetDocumentElementNodeset(CImplInstance * pInstance,IXMLDOMNodeList ** ppRetval)
{
	HRESULT hr;
	CComQIPtr<IXMLDOMDocument> pDoc;
	if(pInstance)
	{
		//if instance exists, get a pointer to its live IXMLDOMDocument2 
		//	member.
		CDOM3Document * pDomLive = pInstance->GetLiveDOM();
		if(pDomLive)
		{
			pDoc = pDomLive->m_pXMLParser;
		}

	}
	else
	{
		//Otherwise, create an empty one, so that we can retturn an empty nodeset.
		pDoc.CoCreateInstance(CLSID_FreeThreadedDOMDocument60);
	}

	if(pDoc)
        hr = pDoc->selectNodes(g_bsPathToDocumentElement,ppRetval);
	else
		hr = E_FAIL;
	
	return hr;
		
}


STDMETHODIMP CXFormsXPathFunctions::globalInstance(BSTR InstanceId,/*[out,retval]*/ IXMLDOMNodeList ** ppRetval)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	HRESULT hr;
	//get the instance
	CImplInstance * pInstance = NULL;
	if(m_pOwnerForm)
	{
		m_pOwnerForm->getInstance(InstanceId,pInstance);
		
		hr = GetDocumentElementNodeset(pInstance,ppRetval);
	}
	else
	{
		hr = E_PENDING;		
	}

	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::instance(BSTR InstanceId, IXMLDOMNodeList **ppRetval)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	HRESULT hr;
	//get the instance
	CImplInstance * pInstance = NULL;
	CImplModel * pActiveModel = m_ActiveModelStack.top();
	if(pActiveModel)
	{
		pInstance = pActiveModel->getInstance(InstanceId);
		hr = GetDocumentElementNodeset(pInstance,ppRetval);
	}
	else
	{
		hr = E_PENDING;		
	}

	return hr;
}
/*
removed: version 24
STDMETHODIMP CXFormsXPathFunctions::serialise(IDispatch *pNodeset, BSTR *sSerialised)
now in different dll.
*/

STDMETHODIMP CXFormsXPathFunctions::daysFromDate(VARIANT vtDate, double *pdblDays)
{
	CComBSTR sDate;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtDate,&sDate);
		if(SUCCEEDED(hr))
			hr = daysFromDate(sDate, pdblDays);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::daysFromDate(BSTR sDate, double *pdblDays)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	SYSTEMTIME st;
	HRESULT hr = E_FAIL;
	if(SUCCEEDED(CDateTime::from_ISO8601(sDate,&st)))
	{
		FILETIME ft;
		//ft is from 01/01/1601, st is from 01/01/1970, we want 01/01/1970
	//	st.wYear -= (1970-1601);

		BOOL bSuccess = ::SystemTimeToFileTime(&st,&ft);
		
		if(bSuccess)
		{
			SYSTEMTIME baseST;
			baseST.wDay = 1;
			baseST.wMonth = 1;
			baseST.wYear = 1970; 

			baseST.wMilliseconds = 0;
			baseST.wSecond = 0;
			baseST.wMinute = 0;
			baseST.wHour = 0;

			
			FILETIME baseFT;
			::SystemTimeToFileTime(&baseST,&baseFT);
			//ft = 100 ns intervals.
			//ns -> ms = ns * 1000 * 1000

			//__int64 uli100ns = (ft.dwHighDateTime << 32) + ft.dwLowDateTime;
			double FULLdbl100ns =  ((double)((ft).dwHighDateTime) * 4.294967296E9 + (double)((ft).dwLowDateTime));
			double BASEdbl100ns =  ((double)((baseFT).dwHighDateTime) * 4.294967296E9 + (double)((baseFT).dwLowDateTime));
			double dbl100ns = FULLdbl100ns - BASEdbl100ns;
			double dblSeconds = dbl100ns / (10 /*=microseconds*/ * 1000 /*=milliseconds*/ * 1000 /*=seconds*/);

			double dblDays = dblSeconds / (/*seconds in a day*/60 * 60 * 24);
			
			//return the whole days.
			*pdblDays = ::floor(dblDays);

			hr = S_OK;
		}
		else
		{
			//NaN if could not understand.
			*pdblDays = std::numeric_limits<double>::quiet_NaN();
			hr = S_FALSE;		
		}
	}
	else
	{
		//NaN if could not understand.
		*pdblDays = std::numeric_limits<double>::quiet_NaN();
		hr = S_FALSE;
	}
	return hr;

}


STDMETHODIMP CXFormsXPathFunctions::secondsFromDateTime(VARIANT vtDate, double *pdblSeconds)
{
	CComBSTR sDate;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtDate,&sDate);
		if(SUCCEEDED(hr))
			hr = secondsFromDateTime(sDate,pdblSeconds);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::secondsFromDateTime(BSTR sDate, double *pdblSeconds)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	SYSTEMTIME st;
	HRESULT hr = E_FAIL;
	if(SUCCEEDED(CDateTime::from_ISO8601(sDate,&st)))
	{
		FILETIME ft;
		//ft is from 01/01/1601, st is from 01/01/1970, we want 01/01/1970
		st.wYear -= (1970-1601);
		BOOL bSuccess = ::SystemTimeToFileTime(&st,&ft);
		if(bSuccess)
		{
			//ft = 100 ns intervals.
			//ns -> ms = ns * 1000 * 1000

			double dbl100ns =  ((double)((ft).dwHighDateTime) * 4.294967296E9 + (double)((ft).dwLowDateTime));
			 
			*pdblSeconds = dbl100ns / (10 /*=microseconds*/ * 1000 /*=milliseconds*/ * 1000 /*=seconds*/);
			hr = S_OK;
		}
		else
		{
			//NaN if could not understand.
			*pdblSeconds = std::numeric_limits<double>::quiet_NaN();
			hr = S_FALSE;		
		}
	}
	else
	{
		//NaN if could not understand.
		*pdblSeconds = std::numeric_limits<double>::quiet_NaN();
		hr = S_FALSE;
	}

	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::seconds(VARIANT vtDuration, double *pdblSeconds)
{
	CComBSTR sDuration;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtDuration,&sDuration);
		if(SUCCEEDED(hr))
			hr = seconds(sDuration,pdblSeconds);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::seconds(BSTR sDuration, double *pdblSeconds)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = E_FAIL;
	SYSTEMTIME st;
	bool bNegative;
	if(SUCCEEDED(CDateTime::from_ISO8601E(sDuration,&st,&bNegative)))
	{
		*pdblSeconds = st.wSecond;
		*pdblSeconds += (st.wMinute * 60);
		*pdblSeconds += (st.wHour * 60*60);
		*pdblSeconds += (st.wDay * 60*60*24);
		*pdblSeconds += ((double)st.wMilliseconds / 1000);
		if(bNegative)
		{
			*pdblSeconds *= -1;
		}
		hr = S_OK;
		
	}
	else
	{
		//NaN if could not understand.
		*pdblSeconds = std::numeric_limits<double>::quiet_NaN();
		hr = S_FALSE;
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::months(VARIANT vtDuration, double *pdblMonths)
{
	CComBSTR sDuration;
	HRESULT hr = E_FAIL;
	try {
		hr = getStringFromVariant(vtDuration,&sDuration);
		if(SUCCEEDED(hr))
			hr = months(sDuration, pdblMonths);
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::months(BSTR sDuration, double *pdblMonths)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	HRESULT hr = E_FAIL;
	SYSTEMTIME st;
	bool bNegative;
	if(SUCCEEDED(CDateTime::from_ISO8601E(sDuration,&st,&bNegative)))
	{

		*pdblMonths = (st.wYear * 12) + st.wMonth;
		if(bNegative)
			*pdblMonths *= -1;

		hr = S_OK;
	}
	else
	{
		//NaN if could not understand.
		*pdblMonths = std::numeric_limits<double>::quiet_NaN();
		hr = S_FALSE;
	}
	return hr;
}

// Fetches a string from a variant argument as passed by XPath, extracts the string value of the first node if nodeset
HRESULT CXFormsXPathFunctions::getStringFromVariant(const VARIANT & vtArg, BSTR * psRetVal) const
{
	HRESULT hr = S_FALSE;
	switch(vtArg.vt)
	{
		//TODO: since we know all domains in which this is used, this SysAllocString may be wasteful.
		//	however, since the string returned from the VT_DISPATCH and VT_R8 cases need to be freed, there needs 
		//	to be some mechanism to inform the caller whether or not to free it.
		case VT_BSTR:
		{
			*psRetVal = ::SysAllocString(vtArg.bstrVal);
			hr = S_OK;
			break;
		}
		case VT_DISPATCH:
		{
			CComQIPtr<IXMLDOMNodeList> pNl = vtArg.pdispVal;
			CComPtr<IXMLDOMNode> pNode;
			hr = pNl->get_item(0,&pNode);
			if(SUCCEEDED(hr) && pNode)
			{
				hr = pNode->get_text(psRetVal);
			}
			break;
		}
		case VT_R8:
		{
			CStringW s;
			s.Format(L"%g",vtArg.dblVal);
			*psRetVal = s.AllocSysString();
			hr = S_OK;
			break;
		}
		case VT_EMPTY:
		case VT_NULL:
			*psRetVal = 0;
			hr = S_FALSE;
			break;

		//TODO: cases for BOOL and numeric values.
		default:
			hr = E_FAIL;
	}
	return hr;
}
 
HRESULT CXFormsXPathFunctions::getNumberFromVariant(const VARIANT & vtArg, double * pdRetval) const
{
	HRESULT hr = S_FALSE;
	switch(vtArg.vt)
	{
		case VT_R8:
		{
			*pdRetval = vtArg.dblVal;
			hr = S_OK;
			break;
		}
		case VT_EMPTY:
		case VT_NULL:
			*pdRetval = 0;
			hr = S_FALSE;
			break;
		default:
		{
			CComBSTR sDoubleAsString;
			getStringFromVariant(vtArg,&sDoubleAsString);
			wchar_t * sStopString;
			*pdRetval = wcstod(sDoubleAsString.m_str,&sStopString);
			if(*sStopString != L'\0')
			{
				*pdRetval = std::numeric_limits<double>::quiet_NaN();
				hr = S_FALSE;
			}
			else
				hr = S_OK;
		}
	}
	return hr;
}

HRESULT CXFormsXPathFunctions::getBooleanFromVariant(const VARIANT & vtArg, bool * pbRetval) const
{
	HRESULT hr = S_FALSE;
	if(VT_BOOL == vtArg.vt)
	{
		*pbRetval = vtArg.boolVal?true:false;
		hr = S_OK;
	}
	else
	{
		CComBSTR sBoolAsString;
		hr = getStringFromVariant(vtArg,&sBoolAsString);
		if(sBoolAsString &&(_wcsicmp(L"true",sBoolAsString) == 0 || _wcsicmp(L"1",sBoolAsString) == 0))
			*pbRetval = true;
		else
			*pbRetval = false;
	}
	return hr;
}

STDMETHODIMP CXFormsXPathFunctions::valid(IXMLDOMNodeList * pNl, BOOL * bIsValid)
{
	HRESULT hr = S_FALSE;
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	try {
		if(pNl && bIsValid) 
		{
			CBoundNode * pBoundNode =0;
			CComPtr<IXMLDOMNode> pNode;
			pNl->get_item(0,&pNode);
			pBoundNode = CBoundNode::GetBoundNode(pNode,0,true);
			if(pBoundNode)
			{
				*bIsValid = pBoundNode->isValid();
			} 
			else
				*bIsValid = VARIANT_TRUE;
			::safeRelease(pBoundNode);
		} 
		else 
		{
			hr = E_INVALIDARG;
		}
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}


STDMETHODIMP CXFormsXPathFunctions::StripDisabledNodes(IXMLDOMNodeList * pNl,IXMLDOMNodeList ** ppRetval)
{
	HRESULT hr = S_FALSE;
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	try {
			CComPtr<IXMLDOMNode> pNode;
			pNl->get_item(0,&pNode);
			if(pNode)
			{
				CComVariant vtOut;
				CComBSTR s;
				pNode->get_xml(&s);
				TransformNodeToRemoveIrrelevantNodes(pNode,&vtOut);
				switch(vtOut.vt)
				{
					case VT_DISPATCH:
					case VT_UNKNOWN:
					{
						CComQIPtr<IXMLDOMNode> pNode = vtOut.punkVal;
						if(pNode)
						{
							pNode->selectNodes(L".",ppRetval);
						}
					}			
				}
			}
			else
				hr = E_INVALIDARG;
	}
	catch(std::bad_alloc) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_MEMEX);
		ASSERT(0);
	}
	catch(...) {
		if(SUCCEEDED(theApp.FormsManager().RegisterEventLog()))
			theApp.FormsManager().WriteEvent(EVENTLOG_ERROR_TYPE, MSG_TOPEX);
		ASSERT(0);
	}
	return hr;
}

void CXFormsXPathFunctions::PushCurrentXPathContext(CNode *pXPathContext,CImplModel * pModel)
{	
	m_XPathContextStack.push(pXPathContext);
	m_ActiveModelStack.push(pModel);
}

void CXFormsXPathFunctions::PopCurrentXPathContext()
{	
	m_XPathContextStack.pop();
	m_ActiveModelStack.pop();
}


HRESULT CXFormsXPathFunctions::TransformNodeToRemoveIrrelevantNodes(IXMLDOMNode * pNode , VARIANT * pvtData)
{
	CComPtr<IXmlState> pStateResolver;
	CImplModel * pActiveModel = m_ActiveModelStack.top();
	HRESULT hr = pActiveModel->m_pMasterDependencyList->GetXMLState(&pStateResolver);
	CComQIPtr<IXmlStateManager> pStateManager = pStateResolver;
	if(pStateManager)
	{
		pStateManager->put_bRelevant(VARIANT_TRUE);
		pStateManager->put_bValidate(VARIANT_FALSE);
	}

	CComBSTR sXML;
	xport::loadHTMLResource(::_Module.m_hInst,"xmlSubmit.xml", sXML);
	VARIANT_BOOL bLoadSuccessful;
	CComPtr<IXMLDOMDocument2> pDOM;
	pDOM.CoCreateInstance(CLSID_FreeThreadedDOMDocument60);
	pDOM->put_async(VARIANT_FALSE);
	hr = pDOM->loadXML(sXML, &bLoadSuccessful);


	CComPtr<IXSLTemplate> pXSLTemplate;
	hr = pXSLTemplate.CoCreateInstance(CLSID_XSLTemplate60);

	hr = pXSLTemplate->putref_stylesheet(pDOM);
	if(SUCCEEDED(hr))
	{
		CComPtr<IXSLProcessor> pProcessor;
		hr = pXSLTemplate->createProcessor(&pProcessor);
		if(SUCCEEDED(hr) && pProcessor)
		{
			CComBSTR sStateResolverNS(L"http://www.x-port.net/xmlstate");
			pProcessor->addObject(pStateResolver,sStateResolverNS);
			hr = pProcessor->put_input(CComVariant(pNode));

			CComVariant vtOutput;
			if(SUCCEEDED(hr))
			{
		//		CComPtr<IStream> pStream = 0;
				// Create a stream
		//		hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);


		//		hr = pStream->QueryInterface(__uuidof(IUnknown), (void **) &pvtData->punkVal);
				CComPtr<IXMLDOMDocument> pDoc;
				pDoc.CoCreateInstance(CLSID_FreeThreadedDOMDocument60);
				pDoc.QueryInterface(&pvtData->punkVal);
				if(SUCCEEDED(hr)) {
					// It need to be attached before we do the 
					// query before the transformation so that 
					// the data gets written to the stream.
					pvtData->vt = VT_UNKNOWN;
					hr = pProcessor->put_output(*pvtData);

					if(SUCCEEDED(hr)) {
						VARIANT_BOOL bTransformSuccessful;
						hr = pProcessor->transform(&bTransformSuccessful);

						if(SUCCEEDED(hr) && bTransformSuccessful == VARIANT_TRUE) {
							hr = pProcessor->get_output(pvtData);
							// Was using this test code to view the 
							// data that had been written
							// Test code
							// HGLOBAL hg = NULL;
							// GetHGlobalFromStream(pStream,&hg);
							// LPVOID output = GlobalLock(hg);
							// GlobalUnlock(hg); 
							// Text code
							
							// We need to reset the file pointer back to the
							// begin so that when the data is read it is read 
							// from the begining not the end.
			/*				ULONG ulRead = 0;
							LARGE_INTEGER liDis;
							liDis.LowPart = liDis.HighPart = 0;
							
							hr = pStream->Seek( liDis, STREAM_SEEK_SET, 0);
			*/			}
					}
				}
			}
			//clean up.
			CComBSTR sNS(L"http://www.x-port.net/xmlstate");
			pProcessor->addObject(0,sNS);
		}
	}
	return hr;
}


	 STDMETHODIMP CXFormsXPathFunctions::power(VARIANT argBase,VARIANT argExponent, double * pdRetval)
	 {
		HRESULT hr = E_FAIL;
		FORMSPLAYER_ENTRY_POINT
		double dBase, dExponent;
		hr = getNumberFromVariant(argBase,&dBase);
		if(S_OK == hr)
		{
			hr = getNumberFromVariant(argExponent,&dExponent);
			if(S_OK == hr)
				*pdRetval = pow(dBase, dExponent);
		}
		if(S_OK != hr)
		{
			*pdRetval = std::numeric_limits<double>::quiet_NaN();
		}
		FORMSPLAYER_EXIT_POINT
		return hr;
	 }		


	STDMETHODIMP CXFormsXPathFunctions::luhn(VARIANT arg1, /*[out,retval]*/ BOOL * pRetval)
	{
		CComBSTR sId;
		HRESULT hr = getStringFromVariant(arg1,&sId);
		
		//Stripping whitespace from the string is not our responsibility.
		//	however, it could be a useful feature if it is not proscribed.
		//	to implement:
		//	(1) rename sId above to bsId.
		//	(2) change sId.Length(); to sId.GetLength();
		//  (3) uncomment the two immediately following lines
	//	CStringW sId(bsId);
	//	sId.Remove(L' ');

		//check the length of the string
		unsigned int len = sId.Length();
		if(len >= g_ui_luhn_min_length && len <= g_ui_luhn_max_length)
		{	// if within promised size, test that it passes the luhn check.
			try
			{	
				CLuhnIDChecker checker;
				*pRetval = checker.checkLuhnID_l(sId,len)?TRUE:FALSE;
			}
			catch(...)
			{
				hr = E_INVALIDARG;
				*pRetval = VARIANT_FALSE;
			}
		}
		else
		{
			*pRetval = VARIANT_FALSE;
		}
		return hr;
	}
	STDMETHODIMP CXFormsXPathFunctions::_id(VARIANT argIDList, IXMLDOMNodeList ** ppRetval){return E_NOTIMPL;}		

const unsigned long iHoursInADay = 24;
const unsigned long iMinutesInAnHour = 60;
const unsigned long iSecondsInAMinute = 60;

STDMETHODIMP CXFormsXPathFunctions::daysToDate(VARIANT argDays, BSTR * pbsRetval)
{
	double dDays;
	HRESULT hr = getNumberFromVariant(argDays,&dDays);

	if(S_OK == hr)
	{
		//Round it appropriately
		long lWholeDays = static_cast<long>((dDays> 0.0) ? dDays + 0.5 : dDays - 0.5);
		//cast it back to a double for the CTor.
		COleDateTimeSpan oDays(static_cast<double>(lWholeDays));
		COleDateTime oT;
		oT.SetDate(1970,1,1);
		oT += oDays;
		CString s = oT.Format("%Y-%m-%d");
		*pbsRetval = s.AllocSysString();
	}
	else
	{
		*pbsRetval =0;
	}

	return hr;
}		

STDMETHODIMP CXFormsXPathFunctions::secondsToDateTime(VARIANT argSeconds, BSTR * pbsRetval)
{
	double dSecs;
	HRESULT hr = getNumberFromVariant(argSeconds,&dSecs);

	if(S_OK == hr)
	{
		//Stupid time_t doesn't support negative time
		time_t t;
		COleDateTimeSpan oSpan;
		if(dSecs >=0)
		{
			 t = static_cast<time_t>(dSecs);
		}
		else
		{
			t = 0;
			dSecs *=-1;
			oSpan = dSecs/(iHoursInADay * iMinutesInAnHour * iSecondsInAMinute);
		}

		COleDateTime oT(t);
		if(oSpan)
		{
			oT -=oSpan;
		}
		CString s = oT.Format("%Y-%m-%dT%H:%M:%SZ");
		*pbsRetval = s.AllocSysString();
		s.Empty();
	}
	else
	{
		*pbsRetval  = 0;
	}
	return hr;
}		

STDMETHODIMP CXFormsXPathFunctions::localDate( BSTR * pbsRetval)
{
	struct tm *newtime;
	time_t t_now = time(0);                
	newtime = localtime( &t_now ); 
	time_t t =  mktime(newtime);
	COleDateTime oT = t;
	CString s = oT.Format("%Y-%m-%d");
	if(_timezone == 0)
	{
		s.Append("Z");
	}
	else
	{
		CTimeSpan ts = _timezone * -1;
		if(_timezone > 0)
			s.Append(ts.Format("%H:%M"));
		else
			s.Append(ts.Format("+%H:%M"));
	}

	*pbsRetval = s.AllocSysString();
	s.Empty();
	return S_OK;
}		

STDMETHODIMP CXFormsXPathFunctions::localDateTime( BSTR * pbsRetval)
{
	struct tm *newtime;
	time_t t_now = time(0);                
	newtime = localtime( &t_now ); 
	time_t t =  mktime(newtime);
	COleDateTime oT = t;
	CString s = oT.Format("%Y-%m-%dT%H:%M:%S");
	if(_timezone == 0)
	{
		s.Append("Z");
	}
	else
	{
		CTimeSpan ts = _timezone * -1;
		if(_timezone > 0)
			s.Append(ts.Format("%H:%M"));
		else
			s.Append(ts.Format("+%H:%M"));
	}
	*pbsRetval = s.AllocSysString();
	
	return S_OK;

}

STDMETHODIMP CXFormsXPathFunctions::encode(VARIANT argData,VARIANT argEncoding, BSTR * pbsRetval){return E_NOTIMPL;}		
STDMETHODIMP CXFormsXPathFunctions::decode(VARIANT argData,VARIANT argEncoding, BSTR * pbsRetval){return E_NOTIMPL;}		
STDMETHODIMP CXFormsXPathFunctions::digest(VARIANT argData, VARIANT argHashingAlgorithm,VARIANT argEncoding, BSTR * pbsRetval){return E_NOTIMPL;}		
STDMETHODIMP CXFormsXPathFunctions::hmac( VARIANT argKey, VARIANT argData, VARIANT argHashingAlgorithm, VARIANT argEncoding, BSTR * pbsRetval){return E_NOTIMPL;}		
	 
	 STDMETHODIMP CXFormsXPathFunctions::random( VARIANT argDoSeeding, double * pdRetval)
	 {
		HRESULT hr = E_FAIL;

		FORMSPLAYER_ENTRY_POINT
			bool bSeed;
			hr = getBooleanFromVariant(argDoSeeding,&bSeed);
			if(SUCCEEDED(hr))
			{
				if(bSeed)
					srand((unsigned)time(NULL));
				int iRand = rand();

				*pdRetval = 1/static_cast<double>(iRand);
				hr = S_OK;
			}
		FORMSPLAYER_EXIT_POINT

		return hr;
	 }	